#ifndef _YDD_HSTRING_H_
#define _YDD_HSTRING_H_

#pragma warning(disable : 4290)

#include <codecvt>
#include <string>
#include <vector>

#include <stdint.h>

#include <Windows.h>

namespace ydd {

template<class _BASE_T>
class hstring {

public:
    hstring() {}
    ~hstring() {}

    hstring(wchar_t _vl) { wchar_t l_vl[2] = {0}; l_vl[0] = _vl; data_ = hstring(l_vl).data_; }
    hstring(char _vl) { char l_vl[2] = {0}; l_vl[0] = _vl; data_ = hstring(l_vl).data_; }
    
    hstring(int8_t _vl) : hstring((int64_t)_vl) {}
    hstring(int16_t _vl) : hstring((int64_t)_vl) {}
    hstring(int32_t _vl) : hstring((int64_t)_vl) {}
    hstring(int64_t _vl) : data_(int_to_string(_vl, 10)){}
    hstring(uint8_t _vl) : hstring((uint64_t)_vl) {}
    hstring(uint16_t _vl) : hstring((uint64_t)_vl) {}
    hstring(uint32_t _vl) : hstring((uint64_t)_vl) {}
    hstring(uint64_t _vl) : data_(uint_to_string(_vl, 10)) {}

    hstring(const char* _string) throw(std::range_error) : hstring(std::string((NULL == _string) ? "" : _string)) {}
    hstring(const std::string& _string) throw(std::range_error) : data_(middle<_BASE_T>::from_utf8_str(_string)) {}
    hstring(const hstring<char>& _string) throw(std::range_error) : hstring(_string.source()) {}

    hstring(const wchar_t* _string) : hstring(std::wstring((NULL == _string) ? L"" : _string)) {}
    hstring(const std::wstring& _string) : data_(middle<_BASE_T>::from_unicode_str(_string)) {}
    hstring(const hstring<wchar_t>& _string) : hstring(_string.source()) {}

public:
    using iterator = typename std::basic_string<_BASE_T>::iterator;
    using const_iterator = typename std::basic_string<_BASE_T>::const_iterator;
    using reverse_iterator = typename std::basic_string<char>::reverse_iterator;
    using const_reverse_iterator = typename std::basic_string<char>::const_reverse_iterator;

public:
    iterator begin() { return data_.begin(); }
    const_iterator begin() const { return data_.begin(); }
    iterator end() { return data_.end(); }
    const_iterator end() const { return data_.end(); }

    reverse_iterator rbegin() { return data_.rbegin(); }
    const_reverse_iterator rbegin() const { return data_.rbegin(); }
    reverse_iterator rend() { return data_.rend(); }
    const_reverse_iterator rend() const { return data_.rend(); }

    const std::basic_string<_BASE_T>& source() const { return data_; }
    const _BASE_T* c_str() const { return data_.c_str(); }

    void clear() { data_.clear(); }
    
    size_t size() const { return data_.size(); }
    size_t length() const { return data_.length(); }
    bool empty() const { return data_.empty(); }

    std::string to_utf8_str() const throw(std::range_error) {
        return middle<_BASE_T>::to_utf8_str(data_);
    }
    std::string to_locale_str(const std::string& _locale = hstring::chcp::hcp()) const throw(std::range_error) {
        return middle<_BASE_T>::to_locale_str(data_, _locale);
    }
    std::wstring to_unicode_str() const throw(std::range_error) {
        return middle<_BASE_T>::to_unicode_str(data_);
    }
        
    hstring& from_unicode_str(const std::wstring& _string) {
        data_ = middle<_BASE_T>::from_unicode_str();
        return *this;
    }
    hstring& from_utf8_str(const std::string& _string) throw(std::range_error) {
        data_ = middle<_BASE_T>::from_utf8_str(_string);
        return *this;
    }
    hstring& from_locale_str(const std::string& _string, const std::string& _locale = hstring::chcp::hcp()) throw(std::range_error) {
        data_ = middle<_BASE_T>::from_locale_str(_string, _locale);
        return *this;
    }
    hstring& from_unicode_str(const wchar_t* _string) {
        data_ = middle<_BASE_T>::from_unicode_str(_string);
        return *this;
    }
    hstring& from_utf8_str(const char* _string) throw(std::range_error) {
        data_ = middle<_BASE_T>::from_utf8_str(_string);
        return *this;
    }
    hstring& from_locale_str(const char* _string, const std::string& _locale = hstring::chcp::hcp()) throw(std::range_error) {
        data_ = middle<_BASE_T>::from_locale_str(_string, _locale);
        return *this;
    }

public:
    bool is_equal(const hstring& _rhs, bool _case) const {
        if (data_.size() != _rhs.size()) {
            return false;
        }
        if (_case) {
            return data_ == _rhs.data_;
        }
        hstring l_vl_1 = data_, l_vl_2 = _rhs;
        l_vl_1.upper();
        l_vl_2.upper();
        return l_vl_1 == l_vl_2;
    }
    bool is_end_by(const hstring& _rhs, bool _case) const {
        if (data_.length() < _rhs.size()) {
            return false;
        }
        hstring l_vl_1 = data_.substr(data_.size() - _rhs.size());
        if (_case) {
            return l_vl_1 == _rhs;
        }
        hstring l_vl_2 = _rhs;
        l_vl_1.upper();
        l_vl_2.upper();
        return l_vl_1 == l_vl_2;
    }
    bool is_begin_by(const hstring& _rhs, bool _case) const {
        if (data_.length() < _rhs.size()) {
            return false;
        }
        hstring l_vl_1 = data_.substr(0, _rhs.size());
        if (_case) {
            return l_vl_1 == _rhs;
        }
        hstring l_vl_2 = _rhs;
        l_vl_1.upper();
        l_vl_2.upper();
        return l_vl_1 == l_vl_2;
    }
    size_t find(const hstring& _rhs, size_t _offset = 0) const {
        return data_.find(_rhs.data_, _offset);
    }
    size_t rfind(const hstring& _rhs, size_t _offset = -1) const {
        return data_.rfind(_rhs.data_, _offset);
    }

    hstring substr(size_t _offset, size_t _count = -1) const {
        return data_.substr(_offset, _count);
    }

    void split(std::vector<hstring>& _list, const hstring& _chars) const {
        int l_begin = 0;
        do {
            int l_index = data_.find(_chars.source(), l_begin);
            if (std::basic_string<_BASE_T>::npos == l_index) {
                break;
            }
            _list.push_back(data_.substr(l_begin, l_index - l_begin));
            l_begin = l_index + _chars.size();
        } while (1);
        if (l_begin != data_.size() - 1) {
            _list.push_back(data_.substr(l_begin, -1));
        }
    }
    void split(std::vector<hstring>& _list, const std::initializer_list<_BASE_T>& _chars) const {
        int l_begin = 0;
        for (int i = 0; i < data_.size(); ++i) {
            if (_chars.end() == std::find(_chars.begin(), _chars.end(), data_[i])) {
                continue;
            }
            _list.push_back(data_.substr(l_begin, i - l_begin));
            l_begin = i + 1;
        }
        if (l_begin != data_.size() - 1) {
            _list.push_back(data_.substr(l_begin, -1));
        }
    }

    hstring replace(const hstring& _src, const hstring& _dest) {
        hstring l_result;
        size_t l_begin = 0;
        while (true) {
            size_t l_index = data_.find(_src.source(), l_begin);
            if (std::basic_string<_BASE_T>::npos == l_index) {
                break;
            }
            l_result << data_.substr(l_begin, l_index - l_begin) << _dest;
            l_begin = l_index + _src.size();
            if (data_.size() == l_begin) {
                break;
            }
        }
        if (data_.size() != l_begin) {
            l_result << data_.substr(l_begin);
        }
        return l_result;
    }

public:
    hstring& from_int(int64_t _vl, int _radix = 10) {
        data_ = int_to_string(_vl, _radix);
        return *this;
    }
    int64_t to_int(int _radix = 10) const {
        return string_to_int(data_, _radix);
    }
    hstring& from_uint(uint64_t _vl, int _radix = 10) {
        data_ = uint_to_string(_vl, _radix);
        return *this;
    }
    uint64_t to_uint(int _radix = 10) {
        return string_to_uint(data_, _radix);
    }

    hstring& upper() {
        to_upper(data_);
        return *this;
    }
    hstring& lower() {
        to_lower(data_);
        return *this;
    }

public:

    static void to_upper(std::basic_string<_BASE_T>& _string) {
        for (int i = 0; i < _string.size(); ++i) {
            if (_string[i] >= 'a' && _string[i] <= 'z') {
                _string[i] = _string[i] - 'a' + 'A';
            }
        }
    }
    static void to_lower(std::basic_string<_BASE_T>& _string) {
        for (int i = 0; i < _string.size(); ++i) {
            if (_string[i] >= 'A' && _string[i] <= 'Z') {
                _string[i] = _string[i] - 'A' + 'a';
            }
        }
    }

    static std::basic_string<_BASE_T> int_to_string(int64_t _vl, int _radix = 10) {
        return middle<_BASE_T>::int_to_string(_vl, _radix);
    }
    static int64_t string_to_int(const std::basic_string<_BASE_T>& _vl, int _radix = 10) {
        return middle<_BASE_T>::string_to_int(_vl, _radix);
    }
    static std::basic_string<_BASE_T> uint_to_string(uint64_t _vl, int _radix = 10) {
        return middle<_BASE_T>::uint_to_string(_vl, _radix);
    }
    static uint64_t string_to_uint(const std::basic_string<_BASE_T>& _vl, int _radix = 10) {
        return middle<_BASE_T>::string_to_uint(_vl, _radix);
    }

    static std::wstring utf8_to_unicode(const char* _string) throw(std::range_error) {
        if (NULL == _string) { return L""; }
        return std::wstring_convert<std::codecvt_utf8<wchar_t>, wchar_t>().from_bytes(_string);
    }
    static std::wstring utf8_to_unicode(const std::string& _string) throw(std::range_error) {
        return std::wstring_convert<std::codecvt_utf8<wchar_t>, wchar_t>().from_bytes(_string);
    }
    static std::string unicode_to_utf8(const wchar_t* _string) {
        if (NULL == _string) { return ""; }
        return std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>, wchar_t>{}.to_bytes(_string);
    }
    static std::string unicode_to_utf8(const std::wstring& _string) {
        return std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>, wchar_t>{}.to_bytes(_string);
    }
    static std::wstring locale_to_unicode(const char* _string, const std::string& _locale = hstring::chcp::hcp()) throw(std::range_error) {
        if (NULL == _string) { return L""; }
        return std::wstring_convert<std::codecvt<wchar_t, char, std::mbstate_t>>(new std::codecvt<wchar_t, char, std::mbstate_t>(_locale.c_str())).from_bytes(_string);
    }
    static std::wstring locale_to_unicode(const std::string& _string, const std::string& _locale = hstring::chcp::hcp()) throw(std::range_error) {
        return std::wstring_convert<std::codecvt<wchar_t, char, std::mbstate_t>>(new std::codecvt<wchar_t, char, std::mbstate_t>(_locale.c_str())).from_bytes(_string);
    }
    static std::string unicode_to_locale(const wchar_t* _string, const std::string& _locale = hstring::chcp::hcp()) {
        if (NULL == _string) { return L""; }
        return std::wstring_convert<std::codecvt<wchar_t, char, std::mbstate_t>>(new std::codecvt<wchar_t, char, std::mbstate_t>(_locale.c_str())).to_bytes(_string);
    }
    static std::string unicode_to_locale(const std::wstring& _string, const std::string& _locale = hstring::chcp::hcp()) {
        return std::wstring_convert<std::codecvt<wchar_t, char, std::mbstate_t>>(new std::codecvt<wchar_t, char, std::mbstate_t>(_locale.c_str())).to_bytes(_string);
    }
    static std::string utf8_to_locale(const char* _string, const std::string& _locale = hstring::chcp::hcp()) throw(std::range_error) {
        if (_locale == ".65001") {
            return _string;
        }
        return unicode_to_locale(utf8_to_unicode(_string), _locale);
    }
    static std::string utf8_to_locale(const std::string& _string, const std::string& _locale = hstring::chcp::hcp()) throw(std::range_error) {
        if (_locale == ".65001") {
            return _string;
        }
        return unicode_to_locale(utf8_to_unicode(_string), _locale);
    }
    static std::string locale_to_utf8(const std::string& _string, const std::string& _locale = hstring::chcp::hcp()) throw(std::range_error) {
        if (_locale == ".65001") {
            return _string;
        }
        return unicode_to_utf8(locale_to_unicode(_string, _locale));
    }
    static std::string locale_to_utf8(const char* _string, const std::string& _locale = hstring::chcp::hcp()) throw(std::range_error) {
        if (_locale == ".65001") {
            return _string;
        }
        return unicode_to_utf8(locale_to_unicode(_string, _locale));
    }

public:

    bool operator==(const hstring& _rhs) const {
        return data_ == _rhs.data_;
    }
    bool operator<(const hstring& _rhs) const {
        return data_ < _rhs.data_;
    }
    bool operator>(const hstring& _rhs) const {
        return data_ > _rhs.data_;
    }
    bool operator!=(const hstring& _rhs) const {
        return !operator==(_rhs);
    }

    bool operator<=(const hstring& _rhs) const {
        return operator<(_rhs) || operator==(_rhs);
    }
    bool operator>=(const hstring& _rhs) const {
        return operator>(_rhs) || operator==(_rhs);
    }
    hstring& operator+=(const hstring& _rhs) {
        data_ += _rhs.data_;
        return *this;
    }
    hstring& operator<<(const hstring& _rhs) {
        data_ += _rhs.data_;
        return *this;
    }
    hstring& operator=(const hstring& _rhs) {
        data_ = _rhs.data_;
        return *this;
    }
    hstring operator+(const hstring& _rhs) const {
        return data_ + _rhs.data_;
    }

    _BASE_T& operator[](int _index) {
        return data_[_index];
    }
    const _BASE_T& operator[](int _index) const {
        return data_[_index];
    }

private:
    std::basic_string<_BASE_T> data_;

public:
    class chcp {
    public:
        static std::string& hcp() {
            static std::string s_hcp_ = std::string(".") + middle<char>::int_to_string((long long)acp());
            return s_hcp_;
        }
        static unsigned int acp() {
            static unsigned int s_acp_ = GetACP();
            return s_acp_;
        }
    };

private:
    template<class _BASE_T>
    class middle;

    template<>
    class middle<char> {
    public:
        static std::basic_string<char> int_to_string(int64_t _vl, int _radix = 10) {
            char l_ch_vl[64] = {0};
            _i64toa_s(_vl, l_ch_vl, sizeof(l_ch_vl) / sizeof(l_ch_vl[0]), _radix);
            return l_ch_vl;
        }
        static int64_t string_to_int(const std::basic_string<char>& _vl, int _radix = 10) {
            return _strtoi64_l(_vl.c_str(), NULL, _radix, 0);
        }
        static std::basic_string<char> uint_to_string(uint64_t _vl, int _radix = 10) {
            char l_ch_vl[64] = {0};
            _ui64toa_s(_vl, l_ch_vl, sizeof(l_ch_vl) / sizeof(l_ch_vl[0]), _radix);
            return l_ch_vl;
        }
        static uint64_t string_to_uint(const std::basic_string<char>& _vl, int _radix = 10) {
            return _strtoull_l(_vl.c_str(), NULL, _radix, 0);
        }

        static std::basic_string<char> from_unicode_str(const wchar_t* _string) {
            return hstring<char>::unicode_to_utf8(_string);
        }
        static std::basic_string<char> from_utf8_str(const char* _string) {
            return _string;
        }
        static std::basic_string<char> from_locale_str(const char* _string, const std::string& _locale) throw(std::range_error) {
            return hstring<char>::locale_to_utf8(_string, _locale);
        }
        static std::string to_utf8_str(const char* _string) {
            return _string;
        }
        static std::string to_locale_str(const char* _string, const std::string& _locale) throw(std::range_error) {
            return hstring<char>::utf8_to_locale(_string, _locale);
        }
        static std::wstring to_unicode_str(const char* _string)  throw(std::range_error) {
            return hstring<char>::utf8_to_unicode(_string);
        }
        static std::basic_string<char> from_unicode_str(const std::wstring& _string) {
            return hstring<char>::unicode_to_utf8(_string);
        }
        static std::basic_string<char> from_utf8_str(const std::string& _string) {
            return _string;
        }
        static std::basic_string<char> from_locale_str(const std::string& _string, const std::string& _locale) throw(std::range_error) {
            return hstring<char>::locale_to_utf8(_string, _locale);
        }
        static std::string to_utf8_str(const std::basic_string<char>& _string) {
            return _string;
        }
        static std::string to_locale_str(const std::basic_string<char>& _string, const std::string& _locale) throw(std::range_error) {
            return hstring<char>::utf8_to_locale(_string, _locale);
        }
        static std::wstring to_unicode_str(const std::basic_string<char>& _string)  throw(std::range_error) {
            return hstring<char>::utf8_to_unicode(_string);
        }
    };

    template<>
    class middle<wchar_t> {
    public:
        static std::basic_string<wchar_t> int_to_string(int64_t _vl, int _radix = 10) {
            wchar_t l_wch_vl[64] = {0};
            _i64tow_s(_vl, l_wch_vl, sizeof(l_wch_vl) / sizeof(l_wch_vl[0]), _radix);
            return l_wch_vl;
        }
        static int64_t string_to_int(const std::basic_string<wchar_t>& _vl, int _radix = 10) {
            return _wcstoll_l(_vl.c_str(), NULL, _radix, 0);
        }
        static std::basic_string<wchar_t> uint_to_string(uint64_t _vl, int _radix = 10) {
            wchar_t l_wch_vl[64] = {0};
            _ui64tow_s(_vl, l_wch_vl, sizeof(l_wch_vl) / sizeof(l_wch_vl[0]), _radix);
            return l_wch_vl;
        }
        static uint64_t string_to_uint(const std::basic_string<wchar_t>& _vl, int _radix = 10) {
            return _wcstoull_l(_vl.c_str(), NULL, _radix, 0);
        }

        static std::basic_string<wchar_t> from_unicode_str(const wchar_t* _string) {
            return _string;
        }
        static std::basic_string<wchar_t> from_utf8_str(const char* _string) throw(std::range_error) {
            return hstring<wchar_t>::utf8_to_unicode(_string);
        }
        static std::basic_string<wchar_t> from_locale_str(const char* _string, const std::string& _locale) throw(std::range_error) {
            return hstring<wchar_t>::locale_to_unicode(_string, _locale);
        }

        static std::string to_utf8_str(const wchar_t* _string) {
            return hstring<wchar_t>::unicode_to_utf8(_string);
        }
        static std::string to_locale_str(const wchar_t* _string, const std::string& _locale) {
            return hstring<wchar_t>::unicode_to_locale(_string, _locale);
        }
        static std::wstring to_unicode_str(const wchar_t* _string) {
            return _string;
        }
        static std::basic_string<wchar_t> from_unicode_str(const std::wstring& _string) {
            return _string;
        }
        static std::basic_string<wchar_t> from_utf8_str(const std::string& _string) throw(std::range_error) {
            return hstring<wchar_t>::utf8_to_unicode(_string);
        }
        static std::basic_string<wchar_t> from_locale_str(const std::string& _string, const std::string& _locale) throw(std::range_error) {
            return hstring<wchar_t>::locale_to_unicode(_string, _locale);
        }
        static std::string to_utf8_str(const std::basic_string<wchar_t>& _string) {
            return hstring<wchar_t>::unicode_to_utf8(_string);
        }
        static std::string to_locale_str(const std::basic_string<wchar_t>& _string, const std::string& _locale) {
            return hstring<wchar_t>::unicode_to_locale(_string, _locale);
        }
        static std::wstring to_unicode_str(const std::basic_string<wchar_t>& _string) {
            return _string;
        }
    };
};

using wstring = hstring<wchar_t>;
using string = hstring<char>;

}   // namespace ydd

#endif